﻿using SkiaSharp;
using System.Text;
using static SkiaSharp.SKColors;

namespace Datav.Design
{
    /// <summary>
    ///
    /// </summary>
    public static class CaptchaUtility
    {
        private static readonly string[] captchaCodeChars = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,m,n,p,q,r,s,t,u,v,w,x,y,z".Split(',');
        private static readonly SKColor[] colors = { Black, Red, Blue, DarkBlue, Green, Orange, Brown, DarkCyan, Purple, HotPink, DeepPink };
        private static readonly SKFontStyleWeight[] fontStyleWeights = { SKFontStyleWeight.Medium, SKFontStyleWeight.SemiBold, SKFontStyleWeight.Bold };
        private static readonly SKFontStyleSlant[] fontStyleSlants = { SKFontStyleSlant.Upright, SKFontStyleSlant.Italic, SKFontStyleSlant.Oblique };
        private static readonly char[] operators = { '+', '-' };

        /// <summary>
        /// 获取图像数字验证码
        /// </summary>
        /// 项目如果是在 windows 服务器下运行则不需要任何安装任何依赖项，如果是在 linux 服务下运行则需要安装 libfontconfig1，如 ubuntu 的安装命令
        /// apt-get update
        /// apt-get -y install libfontconfig1
        /// 如果是采用 docker 模式运行，则需要在 dockerfile 中添加如下配置，该命令适用于 debian 和 ubuntu 的 docker
        /// RUN apt-get update && apt-get -y install libfontconfig1
        /// <returns></returns>
        public static CaptchaResult GetVerifyCode(int width = 0, int height = 30, int length = 4, CaptchaType captchaType = CaptchaType.Normal)
        {
            var (captchaCode, captchaValue) = GenerateRandomCaptchaAsync(length, captchaType);

            width = width == 0 ? captchaCode.Length * 25 + 10 : width;
            Random random = new Random();

            //创建bitmap位图
            using SKBitmap image = new SKBitmap(width, height, SKColorType.Bgra8888, SKAlphaType.Premul);
            //创建画笔
            using SKCanvas canvas = new SKCanvas(image);
            //填充背景颜色为白色
            canvas.DrawColor(White);

            //画图片的背景噪音线
            for (int i = 0; i < (width * height * 0.015); i++)
            {
                using SKPaint drawStyle = new SKPaint();
                drawStyle.Color = new SKColor(Convert.ToUInt32(random.Next()));

                canvas.DrawLine(random.Next(width), random.Next(height), random.Next(width), random.Next(height), drawStyle);
            }

            //将文字写到画布上
            using (SKPaint drawStyle = new SKPaint())
            {
                drawStyle.TextSize = height;
                drawStyle.StrokeWidth = 1;
                //drawStyle.Typeface = SKFontManager.Default.MatchCharacter("Courier", SKFontStyleWeight.SemiBold, SKFontStyleWidth.Normal, SKFontStyleSlant.Oblique, null, '时');
                var emHeight = height * (1 - 0.18F);
                for (var i = 0; i < captchaCode.Length; i++)
                {
                    drawStyle.Color = colors[random.Next(colors.Length)];
                    drawStyle.Typeface = SKTypeface.FromFamilyName("Courier", fontStyleWeights[random.Next(fontStyleWeights.Length)], SKFontStyleWidth.Normal, fontStyleSlants[random.Next(fontStyleSlants.Length)]);
                    var emWidth = (float)width / captchaCode.Length * (i + 1) - (width * (1F / captchaCode.Length));

                    var text = captchaCode.Substring(i, 1);
                    var textWidth = drawStyle.MeasureText(text);
                    if (emWidth + textWidth > width)
                    {
                        emWidth = width - textWidth;
                    }
                    canvas.DrawText(text, emWidth, emHeight, drawStyle);
                }
            }

            //画图片的前景噪音点
            for (int i = 0; i < 1000; i++)
            {
                image.SetPixel(random.Next(width), random.Next(height), new SKColor(Convert.ToUInt32(random.Next())));
            }

            using var img = SKImage.FromBitmap(image);
            using SKData p = img.Encode(SKEncodedImageFormat.Png, 100);

            return new CaptchaResult
            {
                CaptchaCode = captchaCode,
                CaptchaValue = captchaValue,
                CaptchaBytes = p.ToArray(),
                Timestamp = DateTime.Now
            };
        }

        private static (string, string) GenerateRandomCaptchaAsync(int codeLength = 4, CaptchaType captchaType = CaptchaType.Normal)
        {
            var random = new Random();
            if (captchaType == CaptchaType.Normal)
            {
                var temp = -1;

                var captchaCode = new StringBuilder();

                for (int i = 0; i < codeLength; i++)
                {
                    if (temp != -1)
                        random = new Random(i * temp * unchecked((int)DateTime.Now.Ticks));

                    var index = random.Next(captchaCodeChars.Length);

                    if (temp != -1 && temp == index)
                        return GenerateRandomCaptchaAsync(codeLength, captchaType);

                    temp = index;

                    captchaCode.Append(captchaCodeChars[index]);
                }

                var code = captchaCode.ToString();
                return (code, code);
            }

            var r1 = random.Next((int)Math.Pow(10, codeLength - 2));
            var r2Length = codeLength - 1 - r1.ToString().Length;
            var r2 = random.Next((int)Math.Pow(10, r2Length - 1), (int)Math.Pow(10, r2Length));
            var @operator = operators[random.Next(2)];
            var value = @operator == '+' ? $"{r1 + r2}" : Math.Abs(r1 - r2).ToString();
            return ($"{r1}{@operator}{r2}", value);
        }
    }
}